package com.yykj.hospital.server.service.impl;

import com.google.common.reflect.TypeToken;
import com.yykj.common.enums.DoctorArticleShowClient;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.GsonUtil;
import com.yykj.common.util.LocalDateTimeFormatterUtil;
import com.yykj.common.util.LocalObjectUtil;
import com.yykj.common.util.PageRequestUtil;
import com.yykj.common.vo.PageData;
import com.yykj.common.vo.ResultVO;
import com.yykj.feign.client.backstage.BackStageFeignClient;
import com.yykj.feign.client.backstage.dto.AppSettingDTO;
import com.yykj.hospital.client.dto.DoctorArticleDTO;
import com.yykj.hospital.client.dto.DoctorArticleDetail;
import com.yykj.hospital.client.dto.DoctorArticleDetailDTO;
import com.yykj.hospital.client.dto.DoctorInfoDTO;
import com.yykj.hospital.server.converter.DoctorArticleDTOConverter;
import com.yykj.hospital.server.dto.ArticleRollPicListDTO;
import com.yykj.hospital.server.enums.LearningType;
import com.yykj.hospital.server.form.ArticleEditForm;
import com.yykj.hospital.server.form.ArticleSearchForm;
import com.yykj.hospital.server.model.entity.DoctorArticle;
import com.yykj.hospital.server.model.entity.DoctorArticleInterest;
import com.yykj.hospital.server.model.entity.DoctorFriend;
import com.yykj.hospital.server.model.entity.HospitalDoctor;
import com.yykj.hospital.server.model.repository.DoctorArticleInterestRepository;
import com.yykj.hospital.server.model.repository.DoctorArticleRepository;
import com.yykj.hospital.server.model.repository.HospitalDoctorRepository;
import com.yykj.hospital.server.service.ArticleService;
import com.yykj.tencent.im.client.feign.TencentImFeignClient;
import com.yykj.tencent.im.client.form.ContactDTO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl implements ArticleService {
    @Override
    public DoctorArticle editArticle(ArticleEditForm form) {

        DoctorArticle article;


        if (form.getId() != null && form.getId() > 0) {//修改
            article = articleRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            LocalObjectUtil.copyNullValue(form, article);
        } else {
            if (form.getShareCnt() == null) {
                form.setShareCnt(0);
            }
            if (form.getPraiseCnt() == null) {
                form.setPraiseCnt(0);
            }
            if (form.getReadCnt() == null) {
                form.setReadCnt(0);
            }
            if (form.getStatus() == null) {
                form.setStatus(Boolean.TRUE);
            }
            HospitalDoctor doctor = hospitalDoctorRepository.findById(form.getDoctorId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            article = new DoctorArticle();
            BeanUtils.copyProperties(form, article);
            article.setLearningType(LearningType.article);
            article.setDoctorName(doctor.getDoctorName());
            article.setDepId(doctor.getDepId());
            article.setDepName(doctor.getDepName());
            article.setOfficer(doctor.getOfficer());
        }
        article = articleRepository.save(article);
        return article;
    }

    @Override
    public void deleteArticle(Integer id) {
        articleRepository.deleteById(id);
    }

    private List<ContactDTO> getDoctorFriends(String tencentAccountId) {
        String key = "article_doctor_friend_" + tencentAccountId;
        String s = redisService.get(key);
        List<ContactDTO> list;
        if (StringUtils.isEmpty(s)) {
            list = tencentImFeignClient.myFriends(tencentAccountId);
            redisService.set(key, GsonUtil.toJson(list), 600);
        } else {
            list = GsonUtil.fromJson(s, new TypeToken<List<ContactDTO>>() {
            }.getType());
        }
        return list;
    }

    @Override
    public PageData<DoctorArticleDTO> listArticle(ArticleSearchForm form) {
        //查找类型：1自己的文章，2关注的文章，3好友的文章
        List<DoctorArticleInterest> interestList = new ArrayList<>();
        List<DoctorFriend> doctorFriendList = new ArrayList<>();
        if (form.getSearchType() == 2) {//2关注的文章
            interestList = articleInterestRepository.findAllByDoctorId(form.getDoctorId());
            if (interestList.size() == 0) {
                return PageData.defaultPageData(PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()));
            }
        }
        if (form.getSearchType() == 3) {//3好友的文章
            //todo
            HospitalDoctor hospitalDoctor = hospitalDoctorRepository.findById(form.getDoctorId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            List<ContactDTO> contactDTOS = getDoctorFriends(hospitalDoctor.getTencentAccountId());
            if (contactDTOS != null && contactDTOS.size() > 0) {
                List<HospitalDoctor> hospitalDoctorList = hospitalDoctorRepository.findAllByTencentAccountIdIn(contactDTOS.stream().map(ContactDTO::getAccountId).collect(Collectors.toList()));
                if (hospitalDoctorList != null && hospitalDoctorList.size() > 0) {
                    for (HospitalDoctor hsDoctor : hospitalDoctorList) {
                        doctorFriendList.add(DoctorFriend.builder().doctorId(hospitalDoctor.getDoctorId()).friendId(hsDoctor.getDoctorId()).build());
                    }
                }
            }
            if (doctorFriendList.size() == 0) {
                return PageData.defaultPageData(PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()));
            }
        }

        Pageable page = PageRequest.of(PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()), Sort.by(Sort.Direction.DESC, "modifyTime"));

        List<DoctorArticleInterest> finalInterestList = interestList;
        List<DoctorFriend> finalDoctorFriendList = doctorFriendList;
        Page<DoctorArticle> list = articleRepository.findAll(new Specification<DoctorArticle>() {
            @Override
            public Predicate toPredicate(Root<DoctorArticle> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();

                if (form.getSearchType() == 1) {
                    if (form.getHospitalId() != null && form.getHospitalId() > 0) {
                        predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));
                    }
                    if (form.getStatus() != null) {
                        predicateList.add(builder.equal(root.get("status"), form.getStatus()));
                    }
                    if (!StringUtils.isEmpty(form.getDoctorId())) {
                        predicateList.add(builder.equal(root.get("doctorId"), form.getDoctorId()));
                    }

                } else {
                    predicateList.add(builder.equal(root.get("status"), Boolean.TRUE));
                    if (form.getSearchType() == 2) {
                        CriteriaBuilder.In<Integer> in = builder.in(root.get("id").as(Integer.class));
                        for (DoctorArticleInterest i : finalInterestList) {
                            in.value(i.getArticleId());
                        }
                        predicateList.add(in);
                    }
                    if (form.getSearchType() == 3) {
                        CriteriaBuilder.In<String> in = builder.in(root.get("doctorId").as(String.class));
                        for (DoctorFriend doctorFriend : finalDoctorFriendList) {
                            in.value(doctorFriend.getFriendId());
                        }
                        predicateList.add(in);
                    }
                    if (form.getSearchType() == 4) {
                        if (form.getHospitalId() != null && form.getHospitalId() > 0) {
                            predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));
                        }
                        if (!StringUtils.isEmpty(form.getDoctorId())) {
                            predicateList.add(builder.equal(root.get("doctorId"), form.getDoctorId()));
                        }
                    }
                }

                if (!StringUtils.isEmpty(form.getDoctorName())) {
                    Predicate p1 = builder.like(root.get("doctorName"), "%" + form.getDoctorName() + "%");
                    predicateList.add(builder.or(p1));
                }

                if (!StringUtils.isEmpty(form.getTitle())) {
                    Predicate p1 = builder.like(root.get("title"), "%" + form.getTitle() + "%");
                    predicateList.add(builder.or(p1));
                }

                if (!StringUtils.isEmpty(form.getKeyword())) {
                    Predicate p1 = builder.like(root.get("doctorName"), "%" + form.getKeyword() + "%");
                    Predicate p2 = builder.like(root.get("title"), "%" + form.getKeyword() + "%");
                    predicateList.add(builder.or(p1, p2));
                }

                if (form.getClient() != null) {
                    Expression<DoctorArticleShowClient> exp = root.<DoctorArticleShowClient>get("client");
                    if (form.getClient() == DoctorArticleShowClient.patient) {
                        predicateList.add(exp.in(Arrays.asList(DoctorArticleShowClient.patient, DoctorArticleShowClient.all)));
                    }
                    if (form.getClient() == DoctorArticleShowClient.doctor) {
                        predicateList.add(exp.in(Arrays.asList(DoctorArticleShowClient.doctor, DoctorArticleShowClient.all)));
                    }
                    if (form.getClient() == DoctorArticleShowClient.all) {
                        predicateList.add(builder.equal(root.get("client"), DoctorArticleShowClient.all));
                    }
                }

                if (!StringUtils.isEmpty(form.getStartDate())) {
                    LocalDateTime startTime = LocalDateTime.of(LocalDate.parse(form.getStartDate(), LocalDateTimeFormatterUtil.getLocalDateFormatter()), LocalTime.MIN);
                    predicateList.add(builder.greaterThanOrEqualTo(root.get("addTime"), startTime));
                }
                if (!StringUtils.isEmpty(form.getEndDate())) {
                    LocalDateTime endTime = LocalDateTime.of(LocalDate.parse(form.getEndDate(), LocalDateTimeFormatterUtil.getLocalDateFormatter()), LocalTime.MAX);
                    predicateList.add(builder.lessThanOrEqualTo(root.get("addTime"), endTime));
                }

                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        }, page);
        PageData<DoctorArticleDTO> result = new PageData<DoctorArticleDTO>();

        PageData<DoctorArticle> pageData = PageData.formatList(list, form.getCurrentPage(), form.getPageSize());
        BeanUtils.copyProperties(pageData, result);
        result.setList(DoctorArticleDTOConverter.convert(list.getContent()));
        return result;
    }

    @Override
    public DoctorArticle viewArticle(Integer id) {
        DoctorArticle doctorArticle = articleRepository.findById(id).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        doctorArticle.setReadCnt(doctorArticle.getReadCnt() + 1);
        articleRepository.save(doctorArticle);
        return doctorArticle;
    }

    @Override
    public DoctorArticleDetailDTO viewArticleDetail(Integer id) {
        DoctorArticle article = viewArticle(id);
        HospitalDoctor doctor = hospitalDoctorRepository.findById(article.getDoctorId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        DoctorArticleDetailDTO dto = new DoctorArticleDetailDTO();
        DoctorArticleDetail detail = new DoctorArticleDetail();
        BeanUtils.copyProperties(article, detail);
        dto.setArticle(detail);
        DoctorInfoDTO info = new DoctorInfoDTO();
        BeanUtils.copyProperties(doctor, info);
        dto.setDoctorInfo(info);
        return dto;
    }


    @Override
    public void subscribeDoctorArticle(DoctorArticleInterest doctorArticleInterest) {
        DoctorArticleInterest articleInterest = articleInterestRepository.findFirstByDoctorIdAndArticleId(doctorArticleInterest.getDoctorId(), doctorArticleInterest.getArticleId());
        if (articleInterest == null) {
            DoctorArticle doctorArticle = articleRepository.findById(doctorArticleInterest.getArticleId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            if (doctorArticle.getSubCnt() == null) {
                doctorArticle.setSubCnt(0);
            }
            doctorArticle.setSubCnt(doctorArticle.getSubCnt() + 1);
            articleRepository.save(doctorArticle);
            articleInterestRepository.save(doctorArticleInterest);
        }
    }

    @Override
    public void deSubscribeDoctorArticle(DoctorArticleInterest doctorArticleInterest) {
        DoctorArticleInterest articleInterest = articleInterestRepository.findFirstByDoctorIdAndArticleId(doctorArticleInterest.getDoctorId(), doctorArticleInterest.getArticleId());
        if (articleInterest != null) {
            DoctorArticle doctorArticle = articleRepository.findById(doctorArticleInterest.getArticleId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            if (doctorArticle.getSubCnt() > 1) {
                doctorArticle.setSubCnt(doctorArticle.getSubCnt() - 1);
                articleRepository.save(doctorArticle);
            }
            articleInterestRepository.delete(articleInterest);
        }
    }

    @Override
    public DoctorArticleDetailDTO doctorViewArticleDetail(String doctorId, Integer articleId) {
        DoctorArticleDetailDTO doctorArticleDetailDTO = viewArticleDetail(articleId);
        DoctorArticleInterest doctorArticleInterest = articleInterestRepository.findFirstByDoctorIdAndArticleId(doctorId, articleId);
        doctorArticleDetailDTO.setSubscribe(doctorArticleInterest != null);
        return doctorArticleDetailDTO;
    }

    @Override
    public List<ArticleRollPicListDTO> articleRollPicList(Integer hospitalId) {
        ResultVO<AppSettingDTO> setting = backStageFeignClient.viewAppSetting();

        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(0), PageRequestUtil.getSize(setting.getData().getMaxArticleCnt()));
        Page<DoctorArticle> all = articleRepository.findAll(new Specification<DoctorArticle>() {
            @Override
            public Predicate toPredicate(Root<DoctorArticle> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();

                predicateList.add(builder.equal(root.get("hospitalId"), hospitalId));
                predicateList.add(builder.equal(root.get("learningType"), LearningType.article));
                predicateList.add(builder.equal(root.get("status"), Boolean.TRUE));
                predicateList.add(builder.isNotNull(root.get("headImg")));
                predicateList.add(builder.notEqual(root.get("headImg"), ""));
                predicateList.add(
                        builder.or(
                                builder.equal(root.get("client"), DoctorArticleShowClient.all),
                                builder.equal(root.get("client"), DoctorArticleShowClient.doctor)));

                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        }, pageRequest);
        List<ArticleRollPicListDTO> result = new ArrayList<>();
        for (DoctorArticle article : all) {
            ArticleRollPicListDTO dto = new ArticleRollPicListDTO();
            BeanUtils.copyProperties(article, dto);
            result.add(dto);
        }
        return result;
    }

    private BackStageFeignClient backStageFeignClient;

    private DoctorArticleRepository articleRepository;

    private HospitalDoctorRepository hospitalDoctorRepository;

    private DoctorArticleInterestRepository articleInterestRepository;

    private TencentImFeignClient tencentImFeignClient;

    private RedisService redisService;

    @Autowired
    public void setArticleRepository(DoctorArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }

    @Autowired
    public void setHospitalDoctorRepository(HospitalDoctorRepository hospitalDoctorRepository) {
        this.hospitalDoctorRepository = hospitalDoctorRepository;
    }

    @Autowired
    public void setArticleInterestRepository(DoctorArticleInterestRepository articleInterestRepository) {
        this.articleInterestRepository = articleInterestRepository;
    }

    @Autowired
    public void setTencentImFeignClient(TencentImFeignClient tencentImFeignClient) {
        this.tencentImFeignClient = tencentImFeignClient;
    }

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    @Autowired
    public void setBackStageFeignClient(BackStageFeignClient backStageFeignClient) {
        this.backStageFeignClient = backStageFeignClient;
    }
}
